#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

#include "entry.h"
#include "dcore.h"
#include "../crypt/md5.h"
#include "../crypt/b64.h"
#include "../crypt/utils_aes.h"


typedef struct {
	char rev[2];
	md5_byte_t md[16];
	char rev2[2];
	md5_byte_t data_len;
	char data[97];
	char rev3[2];
	uint32_t end;
	uint8_t start;
	uint8_t flag;
	char rev4[2];
} T_INFO, * PT_INFO;

#define HEAD 2989

T_INFO info;
int chk_ok = 0;
jobject core_cl;



char* trans(char * s)
{
	while(*s++) {
		if(*s == '.') *s = '/';
	}
	return s;
}

void decrypt1(const char * s, char *buf)
{
	uint8* d = utils_aesDecrypt((uint8 *)DEF_KEY, (uint8 *)s, strlen(s));
	if(d) {
		strcpy(buf, (const char *)d);
	}
}

jclass native_find_class(JNIEnv* env, jobject self, jstring name)
{
	//Class<?> loadClass(String name)
	jclass cls = env->FindClass("java/lang/ClassLoader");
	jmethodID mid = env->GetMethodID(cls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");

	jsize l = env->GetStringUTFLength(name);
	char buf[128] = {0};
	env->GetStringUTFRegion(name, 0, l, buf);
	trans(buf);

	log_info("find class %s", buf);

	jclass ret;
//	jclass ret = env->FindClass(buf);
//	if(ret == NULL)
	{
		ret = (jclass)env->CallObjectMethod(core_cl, mid, name);
		if(env->ExceptionCheck()) {
			env->ExceptionDescribe();
			env->ExceptionClear();
			log_info("class %s not found", buf);
			return NULL;
		}
	}

	log_info("class %s found ret=%p", buf, ret);

	return ret;
}

/*
int native_load_core1(JNIEnv* env, jobject self)
{
	jobject assetManager;
	AAssetManager* asset = AAssetManager_fromJava(env, g_asset);

	log_info("assm=%p", asset);

	AAsset* a = AAssetManager_open(asset, "drawable/ic_abc_float.png", AASSET_MODE_RANDOM);
	if(!a) {
		log_error("open asset fail");
		return -1;
	}

	off_t l = AAsset_getLength(a);
	log_info("l=%ld", l);
	AAsset_seek(a, HEAD, SEEK_SET);
	void * buf = malloc(l - HEAD);
	int r = AAsset_read(a, buf, l-HEAD);
	log_info("r=%d", r);
	AAsset_close(a);

	memcpy(buf, "\x50\x4B\x03\x04", 4);
}
*/

int native_load_core(JNIEnv* env, jobject self)
{
	//写缓存文件
	char path[128] = {0};
	char opt_path[128] = {0};

	sprintf(path, "%s/core.jar", g_dex_path);
	sprintf(opt_path, "%s", g_dex_path);

//	mkdir(opt_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); //创建目录

	log_info("path=%s", path);
	unlink(path);

	int fd = open(path, O_RDWR|O_CREAT);
	if(fd <= 0) {
		log_error("open file err %d %d", fd, errno);
		return -1;
	}

	lseek(fd, 0, SEEK_SET);
	ssize_t w = write(fd, (void *)dcoreData, sizeof(dcoreData));
	log_info("w=%d err=%d", w, errno);
	close(fd);
	/**
	 * 注意设置权限，默认权限五花八门，会导致 dex 读取失败
	 */
	chmod(path, 0644);

	jclass cls;

	cls = env->FindClass("dalvik/system/DexClassLoader");
	//new DexClassLoader(dexPath, optimizedDirectory, libraryPath, parent)
	jmethodID mid = env->GetMethodID(cls, "<init>",
			"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");//构造函数ID
	core_cl = env->NewObject(cls, mid,
			env->NewStringUTF(path),
			env->NewStringUTF(opt_path),
			(jstring)NULL,
			g_classLoader);//创建java对象

	unlink(path);
	sprintf(path, "%s/core.dex", g_dex_path);
	unlink(path); //删除 odex 试试，没问题

	log_info("core_cl=%p", core_cl);

	/*
	cls = env->FindClass("java/lang/ClassLoader");
	//Class<?> loadClass(String name)
	mid = env->GetMethodID(cls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
	jclass clsInit = (jclass)env->CallObjectMethod(core_cl, mid,
			env->NewStringUTF("com.edroid.autorunner.core.Init"));

	log_info("sadasd");

	//call init()
	mid = env->GetStaticMethodID(clsInit, "asdfs", "()V");
	env->CallStaticVoidMethod(clsInit, mid);
	*/

	//hack classloader
	cls = (env)->FindClass("android/content/ContextWrapper");
	mid = env->GetMethodID(cls, "getBaseContext", "()Landroid/content/Context;");
	jobject base = env->CallObjectMethod(g_application, mid);

	//ContextImpl.LoadedApk
	cls = env->GetObjectClass(base);
	jfieldID fid = env->GetFieldID(cls, "mPackageInfo", "Landroid/app/LoadedApk;");
	jobject mPackageInfo = env->GetObjectField(base, fid);

	//LoadedApk.mClassLoader
	cls = env->GetObjectClass(mPackageInfo);
	fid = env->GetFieldID(cls, "mClassLoader", "Ljava/lang/ClassLoader;");
	jobject mClassLoader = env->GetObjectField(mPackageInfo, fid);

	/*
	//包装并替换此 loader
	memset(path, 0, sizeof(path));
	decrypt1("oEoz8+/L/hoD9K7qqzkOqni3erSscIegv0HmzHH6pQo=", path);
	cls = env->FindClass(path);

	memset(path, 0, sizeof(path));
	decrypt1("boi+oFt7+Pm9UXz70lXuD1V5KJM9FgvkkROCE4kPYtp0pPzOrZjAqjO67TWRVu+8BEEAk1EZ4FtrUHinO9z32Q==", path);
	mid = env->GetMethodID(cls, "<init>", path);
	jobject mycl = env->NewObject(cls, mid, mClassLoader, core_cl);

	log_info("mycl=%p", mycl);
	env->SetObjectField(mPackageInfo, fid, mycl); //替换
	*/

	env->SetObjectField(mPackageInfo, fid, core_cl); //替换

	env->DeleteLocalRef(base);
	env->DeleteLocalRef(mPackageInfo);
	env->DeleteLocalRef(mClassLoader);
//	env->DeleteLocalRef(mycl);

	return 0;
}

int chk(const char *path)
{
	int fd = open(path, O_RDONLY);
//	T_INFO info = {0};
	int ok = 0;

	lseek(fd, -128, SEEK_END);
	if(-1 != read(fd, &info, sizeof(T_INFO))) {
		if(!info.flag) {
			log_warn("no sig!");
			ok = -1;
			goto end;
		}

		md5_state_t md = {0};
		char str[64] = {0};

		log_info("src=%s", md5_tostring(info.md, str, 64));
		log_info("start=%x, end=%x", info.start, info.end);

		md5_init(&md);

		md5_byte_t buf[1024];
		int l, m = sizeof(buf), s = info.start;

		lseek(fd, info.start, SEEK_SET);
		while ((l = read(fd, buf, m)) != -1) {
			if(s >= info.end) break;

			s += l;
			md5_append(&md, buf, l);
			m = (s + sizeof(buf) <= info.end)? sizeof(buf) : info.end-s;
		}

		md5_byte_t dst[16] = {0};
		md5_finish(&md, dst);

		log_info("dst=%s", md5_tostring(dst, str, 64));

		for(l=0; l<16; l++) {
			if(dst[l] != info.md[l])
				goto end;
		}

		ok = 1;
		chk_ok = 1;
	} else {
		log_error("read err, code=%d", errno);
	}

end:
	close(fd);

	return ok;
}

int native_check(JNIEnv* env, jobject self, jstring path) {
	int ok = 0;
	const char * str = env->GetStringUTFChars(path, JNI_FALSE);
	if(str) {
		ok = chk(str);
		env->ReleaseStringUTFChars(path, str);
	}

	return ok;
}

int native_boot(JNIEnv * env, jobject self) {
	jclass cls = env->FindClass("android/app/Application");

	//String getPackageResourcePath()
	jmethodID id = (env)->GetMethodID(cls, "getPackageResourcePath", "()Ljava/lang/String;");
	jstring path = (jstring)(env)->CallObjectMethod(g_application, id);

	int ok = native_check(env, self, path);
	if(ok != 1) {
		log_error("chk fail!");
		exit(0);
	}

	//加载代码
	return ok;
}

jstring native_get_data(JNIEnv * env, jobject self) {
	if(!chk_ok) return NULL;

	size_t len = 0;
	int off = (info.data[0]-'A');

	LOGI("len=%d, off=%d, datas=%s", info.data_len, off, info.data);

	unsigned char * str = base64_decode((unsigned char *)((char *)info.data + off), (size_t)info.data_len-off, &len);
	LOGI("data=%s len=%d", str, len);
	jstring data = (env)->NewStringUTF((char *)str);
	free(str);

	return data;
}

void native_setcl(JNIEnv * env, jobject self, jobject cl1, jobject cl2) {
	jclass cls = (env)->FindClass("java/lang/ClassLoader");
	jfieldID fid = (env)->GetFieldID(cls, "parent", "Ljava/lang/ClassLoader;");

	jobject tmp = (env)->GetObjectField(cl2, fid);
	(env)->SetObjectField(cl1, fid, tmp);
	(env)->SetObjectField(cl2, fid, cl1);
}

jstring native_encrypt1(JNIEnv * env, jobject self, jstring s)
{
	jstring ret = NULL;

	const char * str = (env)->GetStringUTFChars(s, JNI_FALSE);
//	log_info("src=%s l=%d", str, strlen(str));
	if(str) {
		uint8 *enc = utils_aesEncrypt((uint8*)DEF_KEY, (uint8*)str, strlen(str));
		ret = (env)->NewStringUTF((char *)enc);
//		log_info("enc=%s", enc);
		free(enc);
		(env)->ReleaseStringUTFChars(s, str);
	}

	return ret;
}

jstring native_encrypt(JNIEnv * env, jobject self, jstring s, jstring k)
{
	jstring ret = NULL;

	const char * str = (env)->GetStringUTFChars(s, JNI_FALSE);
	if(str) {
		const char* key = (env)->GetStringUTFChars(k, JNI_FALSE);
		if(key) {
			uint8 *enc = utils_aesEncrypt((uint8*)key, (uint8*)str, strlen(str));
			ret = (env)->NewStringUTF((char *)enc);

			free(enc);
			(env)->ReleaseStringUTFChars(k, key);
		}

		(env)->ReleaseStringUTFChars(s, str);
	}

	return ret;
}

jstring native_decrypt(JNIEnv * env, jobject self, jstring s, jstring k)
{
	jstring ret = NULL;

	const char * str = (env)->GetStringUTFChars(s, JNI_FALSE);
	if(str) {
		const char* key = (env)->GetStringUTFChars(k, JNI_FALSE);
		if(key) {
			uint8 *dec = utils_aesDecrypt((uint8*)key, (uint8*)str, strlen(str));
			ret = (env)->NewStringUTF((char *)dec);

			free(dec);
			(env)->ReleaseStringUTFChars(k, key);
		}

		(env)->ReleaseStringUTFChars(s, str);
	}

	return ret;
}

jstring native_decrypta(JNIEnv * env, jobject self, jstring s)
{
	jstring ret = NULL;

	const char * str = (env)->GetStringUTFChars(s, JNI_FALSE);
	if(str) {
		uint8 *dec = utils_aesDecrypt((uint8*)DEF_KEY, (uint8*)str, strlen(str));
		ret = (env)->NewStringUTF((char *)dec);
		free(dec);
		(env)->ReleaseStringUTFChars(s, str);
	}

	return ret;
}

jstring native_decryptc(JNIEnv * env, jobject self, jstring s)
{
	jstring ret = NULL;

	const char * str = (env)->GetStringUTFChars(s, JNI_FALSE);
	if(str) {
		uint8 *dec = utils_aesDecrypt3((uint8*)str, strlen(str));
		ret = (env)->NewStringUTF((char *)dec);
		free(dec);
		(env)->ReleaseStringUTFChars(s, str);
	}

	return ret;
}

